/**
* Copyright 2014-2017 yangming.liu<bytefox@126.com>.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see <http://www.gnu.org/licenses/>.
*/
package org.bytesoft.bytetcc.supports.springcloud;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.supports.wire.RemoteCoordinator;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
public class SpringCloudCoordinator implements InvocationHandler {
static final Logger logger = LoggerFactory.getLogger(SpringCloudCoordinator.class);
private String identifier;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> clazz = method.getDeclaringClass();
String methodName = method.getName();
if (Object.class.equals(clazz)) {
return method.invoke(this, args);
} else if (RemoteCoordinator.class.equals(clazz)) {
if ("getIdentifier".equals(methodName)) {
return this.identifier;
} else {
throw new XAException(XAException.XAER_RMFAIL);
}
} else if (XAResource.class.equals(clazz)) {
if ("prepare".equals(methodName)) {
return this.invokePostCoordinator(proxy, method, args);
} else if ("commit".equals(methodName)) {
return this.invokePostCoordinator(proxy, method, args);
} else if ("rollback".equals(methodName)) {
return this.invokePostCoordinator(proxy, method, args);
} else if ("recover".equals(methodName)) {
return this.invokeGetCoordinator(proxy, method, args);
} else if ("forget".equals(methodName)) {
return this.invokePostCoordinator(proxy, method, args);
} else {
throw new XAException(XAException.XAER_RMFAIL);
}
} else {
throw new IllegalAccessException();
}
}
public Object invokePostCoordinator(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> returnType = method.getReturnType();
try {
int firstIndex = this.identifier.indexOf(":");
int lastIndex = this.identifier.lastIndexOf(":");
String prefix = firstIndex <= 0 ? null : this.identifier.substring(0, firstIndex);
String suffix = lastIndex <= 0 ? null : this.identifier.substring(lastIndex + 1);
String instanceId = prefix == null || suffix == null ? null : prefix + ":" + suffix;
StringBuilder ber = new StringBuilder();
ber.append("http://");
ber.append(instanceId);
ber.append("/org/bytesoft/bytetcc/");
ber.append(method.getName());
for (int i = 0; i < args.length; i++) {
Serializable arg = (Serializable) args[i];
ber.append("/").append(this.serialize(arg));
}
ResponseEntity<?> response = new RestTemplate().postForEntity(ber.toString(), null, returnType, new Object[0]);
return response.getBody();
} catch (HttpClientErrorException ex) {
throw new XAException(XAException.XAER_RMFAIL);
} catch (HttpServerErrorException ex) {
// int statusCode = ex.getRawStatusCode();
HttpHeaders headers = ex.getResponseHeaders();
String failureText = StringUtils.trimToNull(headers.getFirst("failure"));
String errorText = StringUtils.trimToNull(headers.getFirst("XA_XAER"));
Boolean failure = failureText == null ? null : Boolean.parseBoolean(failureText);
Integer errorCode = null;
try {
errorCode = errorText == null ? null : Integer.parseInt(errorText);
} catch (Exception ignore) {
logger.debug(ignore.getMessage());
}
if (failure != null && errorCode != null) {
throw new XAException(errorCode);
} else {
throw new XAException(XAException.XAER_RMERR);
}
} catch (Exception ex) {
throw new XAException(XAException.XAER_RMERR);
}
}
public Object invokeGetCoordinator(Object proxy, Method method, Object[] args) throws Throwable {
Class<?> returnType = method.getReturnType();
try {
StringBuilder ber = new StringBuilder();
ber.append("http://");
ber.append(this.identifier);
ber.append("/org/bytesoft/bytetcc/");
ber.append(method.getName());
for (int i = 0; i < args.length; i++) {
Serializable arg = (Serializable) args[i];
ber.append("/").append(this.serialize(arg));
}
ResponseEntity<?> response = new RestTemplate().getForEntity(ber.toString(), returnType, new Object[0]);
return response.getBody();
} catch (HttpClientErrorException ex) {
throw new XAException(XAException.XAER_RMFAIL);
} catch (HttpServerErrorException ex) {
HttpHeaders headers = ex.getResponseHeaders();
String failureText = StringUtils.trimToNull(headers.getFirst("failure"));
String errorText = StringUtils.trimToNull(headers.getFirst("XA_XAER"));
Boolean failure = failureText == null ? null : Boolean.parseBoolean(failureText);
Integer errorCode = null;
try {
errorCode = errorText == null ? null : Integer.parseInt(errorText);
} catch (Exception ignore) {
logger.debug(ignore.getMessage());
}
if (failure != null && errorCode != null) {
throw new XAException(errorCode);
} else {
throw new XAException(XAException.XAER_RMERR);
}
} catch (Exception ex) {
throw new XAException(XAException.XAER_RMERR);
}
}
private String serialize(Serializable arg) throws IOException {
if (Xid.class.isInstance(arg)) {
Xid xid = (Xid) arg;
byte[] globalTransactionId = xid.getGlobalTransactionId();
return ByteUtils.byteArrayToString(globalTransactionId);
} else if (Integer.class.isInstance(arg) || Integer.TYPE.isInstance(arg)) {
return String.valueOf(arg);
} else if (Boolean.class.isInstance(arg) || Boolean.TYPE.isInstance(arg)) {
return String.valueOf(arg);
} else {
byte[] byteArray = CommonUtils.serializeObject(arg);
return ByteUtils.byteArrayToString(byteArray);
}
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
}